package com.working.libview.widget.banner

import android.content.Context
import android.os.Handler
import android.os.Looper
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.animation.Interpolator
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.RelativeLayout
import android.widget.Scroller
import androidx.annotation.DrawableRes
import androidx.core.view.updateLayoutParams
import androidx.viewpager.widget.PagerAdapter
import androidx.viewpager.widget.ViewPager
import com.working.libview.R
import com.working.libview.widget.banner.holder.MZHolderCreator
import com.working.libview.widget.banner.holder.MZViewHolder
import com.working.libview.widget.banner.transformer.CoverModeTransformer
import com.working.libview.widget.banner.transformer.ScaleYTransformer
import dp
import java.lang.Exception
import java.util.*

typealias BannerPageClickListener = (view: View, position: Int) -> Unit

class BannerView : RelativeLayout {
    private val mViewPager: CustomViewPager
    private var mAdapter: BannerAdapter<*>? = null
    private var mData: List<*>? = null
    private var mIsAutoPlay = true // 是否自动播放
    private var mCurrentItem = 0 //当前位置
    private val mHandler = Handler(Looper.getMainLooper())
    private var mDelayedTime = 3000 // Banner 切换时间间隔
    private val mViewPagerScroller by lazy {
        ViewPagerScroller(
            context
        )
    }
    private var mIsOpenEffect = true
    private var mIsCanLoop = true // 是否轮播图片
    private val indicatorContainer: LinearLayout //indicator容器
    private val mIndicators =
        ArrayList<ImageView>()

    //mIndicatorRes[0] 为为选中，mIndicatorRes[1]为选中
    private val mIndicatorRes =
        intArrayOf(R.drawable.indicator_normal, R.drawable.indicator_selected)
    private var mIndicatorPaddingLeft = 0 // indicator 距离左边的距离
    private var mIndicatorPaddingRight = 0 //indicator 距离右边的距离
    private var mIndicatorPaddingTop = 0 //indicator 距离上边的距离
    private var mIndicatorPaddingBottom = 0 //indicator 距离下边的距离
    private var mMZModePadding = 30.dp//在仿魅族模式下，由于前后显示了上下一个页面的部分，因此需要计算这部分padding
    private var mIndicatorAlign = 1
    private var mOnPageChangeListener: ViewPager.OnPageChangeListener? = null
    private var mBannerPageClickListener: BannerPageClickListener? = null

    enum class IndicatorAlign {
        LEFT,  //做对齐
        CENTER,  //居中对齐
        RIGHT //右对齐
    }

    /**
     * 中间Page是否覆盖两边，默认覆盖
     */
    private var mIsMiddlePageCover = true

    constructor(context: Context) : super(context)

    constructor(
        context: Context,
        attrs: AttributeSet
    ) : super(context, attrs) {
        readAttrs(context, attrs)

    }

    constructor(
        context: Context,
        attrs: AttributeSet,
        defStyleAttr: Int
    ) : super(context, attrs, defStyleAttr) {
        readAttrs(context, attrs)
    }

    constructor(
        context: Context,
        attrs: AttributeSet,
        defStyleAttr: Int,
        defStyleRes: Int
    ) : super(context, attrs, defStyleAttr, defStyleRes) {
        readAttrs(context, attrs)

    }

    private fun readAttrs(
        context: Context,
        attrs: AttributeSet
    ) {
        val typedArray = context.obtainStyledAttributes(attrs, R.styleable.BannerView)
        mIsOpenEffect = typedArray.getBoolean(R.styleable.BannerView_open_cool_mode, true)
        mIsMiddlePageCover = typedArray.getBoolean(R.styleable.BannerView_middle_page_cover, true)
        mIsCanLoop = typedArray.getBoolean(R.styleable.BannerView_canLoop, true)
        mIndicatorAlign = typedArray.getInt(
            R.styleable.BannerView_indicatorAlign,
            IndicatorAlign.CENTER.ordinal
        )
        mIndicatorPaddingLeft =
            typedArray.getDimensionPixelSize(R.styleable.BannerView_indicatorPaddingLeft, 0)
        mIndicatorPaddingRight =
            typedArray.getDimensionPixelSize(R.styleable.BannerView_indicatorPaddingRight, 0)
        mIndicatorPaddingTop =
            typedArray.getDimensionPixelSize(R.styleable.BannerView_indicatorPaddingTop, 0)
        mIndicatorPaddingBottom =
            typedArray.getDimensionPixelSize(R.styleable.BannerView_indicatorPaddingBottom, 0)
        typedArray.recycle()
    }

    init {
        clipChildren = false
        mViewPager = CustomViewPager(context)
        mViewPager.clipChildren = false
        indicatorContainer = LinearLayout(context)
        mViewPager.offscreenPageLimit = 4
        addView(mViewPager, LayoutParams(LayoutParams.MATCH_PARENT, 200.dp).apply {
            marginEnd = 15.dp
            marginStart = 15.dp
        })
        addView(indicatorContainer)
        // 初始化Scroller
        initViewPagerScroll()
        sureIndicatorPosition()
    }

    /**
     * 是否开启魅族模式
     */
    private fun setOpenEffect() {
        // 魅族模式
        if (mIsOpenEffect) {
            if (mIsMiddlePageCover) {
                // 中间页面覆盖两边，和魅族APP 的banner 效果一样。
                mViewPager.setPageTransformer(
                    true,
                    CoverModeTransformer(mViewPager)
                )
            } else {
                // 中间页面不覆盖，页面并排，只是Y轴缩小
                mViewPager.setPageTransformer(false, ScaleYTransformer())
            }
        }
    }

    /**
     * make sure the indicator
     */
    private fun sureIndicatorPosition() {
        when (mIndicatorAlign) {
            IndicatorAlign.LEFT.ordinal -> {
                setIndicatorAlign(IndicatorAlign.LEFT)
            }
            IndicatorAlign.CENTER.ordinal -> {
                setIndicatorAlign(IndicatorAlign.CENTER)
            }
            else -> {
                setIndicatorAlign(IndicatorAlign.RIGHT)
            }
        }
    }

    /**
     * 设置ViewPager的滑动速度
     */
    private fun initViewPagerScroll() {
        try {
            val mScroller = ViewPager::class.java.getDeclaredField("mScroller")
            mScroller.isAccessible = true
            mScroller[mViewPager] = mViewPagerScroller
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    private val mLoopRunnable: Runnable = object : Runnable {
        override fun run() {
            if (mIsAutoPlay) {
                mCurrentItem = mViewPager.currentItem
                mCurrentItem++
                if (mCurrentItem == mAdapter!!.count - 1) {
                    mCurrentItem = 0
                    mViewPager.setCurrentItem(mCurrentItem, false)
                    mHandler.postDelayed(this, mDelayedTime.toLong())
                } else {
                    mViewPager.currentItem = mCurrentItem
                    mHandler.postDelayed(this, mDelayedTime.toLong())
                }
            } else {
                mHandler.postDelayed(this, mDelayedTime.toLong())
            }
        }
    }

    /**
     * 初始化指示器Indicator
     */
    private fun initIndicator() {
        indicatorContainer.removeAllViews()
        mIndicators.clear()
        for (i in mData!!.indices) {
            val imageView = ImageView(context)
            if (mIndicatorAlign == IndicatorAlign.LEFT.ordinal) {
                if (i == 0) {
                    val paddingLeft =
                        if (mIsOpenEffect) mIndicatorPaddingLeft + mMZModePadding else mIndicatorPaddingLeft
                    imageView.setPadding(paddingLeft + 6, 0, 6, 0)
                } else {
                    imageView.setPadding(6, 0, 6, 0)
                }
            } else if (mIndicatorAlign == IndicatorAlign.RIGHT.ordinal) {
                if (i == mData!!.size - 1) {
                    val paddingRight =
                        if (mIsOpenEffect) mMZModePadding + mIndicatorPaddingRight else mIndicatorPaddingRight
                    imageView.setPadding(6, 0, 6 + paddingRight, 0)
                } else {
                    imageView.setPadding(6, 0, 6, 0)
                }
            } else {
                imageView.setPadding(6, 0, 6, 0)
            }
            if (i == mCurrentItem % mData!!.size) {
                imageView.setImageResource(mIndicatorRes[1])
            } else {
                imageView.setImageResource(mIndicatorRes[0])
            }
            mIndicators.add(imageView)
            indicatorContainer.addView(imageView)
        }
    }

    override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
        if (!mIsCanLoop) {
            return super.dispatchTouchEvent(ev)
        }
        when (ev.action) {
            MotionEvent.ACTION_MOVE, MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_OUTSIDE, MotionEvent.ACTION_DOWN -> {
                val paddingLeft = mViewPager.left
                val touchX = ev.rawX
                // 如果是魅族模式，去除两边的区域
                if (touchX >= paddingLeft && touchX < getScreenWidth(
                        context
                    ) - paddingLeft
                ) {
                    pause()
                }
            }
            MotionEvent.ACTION_UP -> start()
        }
        return super.dispatchTouchEvent(ev)
    }
    /** */
    /**                             对外API                                                                */
    /** */
    /**
     * 开始轮播
     *
     * 应该确保在调用用了[{][.setPages] 之后调用这个方法开始轮播
     */
    fun start() {
        if (mAdapter == null) {
            return
        }
        if (mIsCanLoop) {
            pause()
            mIsAutoPlay = true
            mHandler.postDelayed(mLoopRunnable, mDelayedTime.toLong())
        }
    }

    /**
     * 停止轮播
     */
    fun pause() {
        mIsAutoPlay = false
        mHandler.removeCallbacks(mLoopRunnable)
    }

    /**
     * 设置是否可以轮播
     *
     * @param canLoop
     */
    fun setCanLoop(canLoop: Boolean) {
        mIsCanLoop = canLoop
        if (!canLoop) {
            pause()
        }
    }

    /**
     * 设置BannerView 的切换时间间隔
     *
     * @param delayedTime
     */
    fun setDelayedTime(delayedTime: Int) {
        mDelayedTime = delayedTime
    }

    fun addPageChangeListener(onPageChangeListener: ViewPager.OnPageChangeListener) {
        mOnPageChangeListener = onPageChangeListener
    }

    /**
     * 添加Page点击事件
     *
     * @param bannerPageClickListener [BannerPageClickListener]
     */
    fun setBannerPageClickListener(bannerPageClickListener: BannerPageClickListener?) {
        mBannerPageClickListener = bannerPageClickListener
    }

    /**
     * 是否显示Indicator
     *
     * @param visible true 显示Indicator，否则不显示
     */
    fun setIndicatorVisible(visible: Boolean) {
        if (visible) {
            indicatorContainer!!.visibility = View.VISIBLE
        } else {
            indicatorContainer!!.visibility = View.GONE
        }
    }

    /**
     * set indicator padding
     *
     * @param paddingLeft
     * @param paddingTop
     * @param paddingRight
     * @param paddingBottom
     */
    fun setIndicatorPadding(
        paddingLeft: Int,
        paddingTop: Int,
        paddingRight: Int,
        paddingBottom: Int
    ) {
        mIndicatorPaddingLeft = paddingLeft
        mIndicatorPaddingTop = paddingTop
        mIndicatorPaddingRight = paddingRight
        mIndicatorPaddingBottom = paddingBottom
        sureIndicatorPosition()
    }

    /**
     * 返回ViewPager
     *
     * @return [ViewPager]
     */
    val viewPager: ViewPager?
        get() = mViewPager

    /**
     * 设置indicator 图片资源
     *
     * @param unSelectRes 未选中状态资源图片
     * @param selectRes   选中状态资源图片
     */
    fun setIndicatorRes(@DrawableRes unSelectRes: Int, @DrawableRes selectRes: Int) {
        mIndicatorRes[0] = unSelectRes
        mIndicatorRes[1] = selectRes
    }

    /**
     * 设置数据，这是最重要的一个方法。
     *
     * 其他的配置应该在这个方法之前调用
     *
     * @param datas           Banner 展示的数据集合
     * @param mzHolderCreator ViewHolder生成器 [MZHolderCreator] And [MZViewHolder]
     */
    fun <T> setPages(
        datas: List<T>?,
        factory: BannerFactory<T>
    ) {

        if (datas.isNullOrEmpty()) return
        mData = datas

        //如果在播放，就先让播放停止
        pause()

        //增加一个逻辑：由于魅族模式会在一个页面展示前后页面的部分，因此，数据集合的长度至少为3,否则，自动为普通Banner模式
        //不管配置的:open_mz_mode 属性的值是否为true
        if (datas.size < 3) {
            mIsOpenEffect = false
            val layoutParams =
                mViewPager.layoutParams as MarginLayoutParams
            layoutParams.setMargins(0, 0, 0, 0)
            mViewPager.layoutParams = layoutParams
            clipChildren = true
            mViewPager.clipChildren = true
        }
        setOpenEffect()
        // 2017.7.20 fix：将Indicator初始化放在Adapter的初始化之前，解决更新数据变化更新时crush.
        //初始化Indicator
        initIndicator()
        mAdapter = BannerAdapter(datas, factory, mIsCanLoop)
        mAdapter?.setUpViewViewPager(mViewPager)
        mAdapter?.setPageClickListener(mBannerPageClickListener)
        mViewPager.clearOnPageChangeListeners()
        mViewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
            override fun onPageScrolled(
                position: Int,
                positionOffset: Float,
                positionOffsetPixels: Int
            ) {
                val realPosition = position % mIndicators.size
                mOnPageChangeListener?.onPageScrolled(
                    realPosition,
                    positionOffset,
                    positionOffsetPixels
                )
            }

            override fun onPageSelected(position: Int) {
                mCurrentItem = position


                // 切换indicator
                val realSelectPosition = mCurrentItem % mIndicators.size
                for (i in datas.indices) {
                    if (i == realSelectPosition) {
                        mIndicators[i].setImageResource(mIndicatorRes[1])
                    } else {
                        mIndicators[i].setImageResource(mIndicatorRes[0])
                    }
                }
                // 不能直接将mOnPageChangeListener 设置给ViewPager ,否则拿到的position 是原始的position
                mOnPageChangeListener?.onPageSelected(realSelectPosition)
            }

            override fun onPageScrollStateChanged(state: Int) {
                when (state) {
                    ViewPager.SCROLL_STATE_DRAGGING -> mIsAutoPlay = false
                    ViewPager.SCROLL_STATE_SETTLING -> mIsAutoPlay = true
                }
                mOnPageChangeListener?.onPageScrollStateChanged(state)
            }
        })
    }

    /**
     * 设置Indicator 的对齐方式
     *
     * @param indicatorAlign [IndicatorAlign.CENTER][IndicatorAlign.LEFT][IndicatorAlign.RIGHT]
     */
    fun setIndicatorAlign(indicatorAlign: IndicatorAlign) {
        mIndicatorAlign = indicatorAlign.ordinal
        indicatorContainer.updateLayoutParams<LayoutParams> {
            addRule(ALIGN_PARENT_BOTTOM)
            when (indicatorAlign) {
                IndicatorAlign.LEFT -> {
                    addRule(ALIGN_PARENT_LEFT)
                }
                IndicatorAlign.RIGHT -> {
                    addRule(ALIGN_PARENT_RIGHT)
                }
                else -> {
                    addRule(CENTER_HORIZONTAL)
                }
            }

            setMargins(0, mIndicatorPaddingTop, 0, mIndicatorPaddingBottom)
        }

    }

    /**
     * 设置是否使用ViewPager默认是的切换速度
     *
     * @param useDefaultDuration 切换动画时间
     */
    fun setUseDefaultDuration(useDefaultDuration: Boolean) {
        mViewPagerScroller?.useDefaultDuration = useDefaultDuration
    }

    /**
     * 获取Banner页面切换动画时间
     *
     * @return
     */
    /**
     * 设置ViewPager切换的速度
     *
     * @param duration 切换动画时间
     */
    var duration: Int
        get() = mViewPagerScroller.scrollDuration
        set(duration) {
            mViewPagerScroller.duration = duration
        }

    class BannerAdapter<T>(
        datas: List<T>,
        val factory: BannerFactory<T>,
        val canLoop: Boolean
    ) : PagerAdapter() {
        private var mData: MutableList<T> = arrayListOf()
        private var mViewPager: ViewPager? = null
        private var mPageClickListener: BannerPageClickListener? = null
        private val mLooperCountFactor = 500
        fun setPageClickListener(pageClickListener: BannerPageClickListener?) {
            mPageClickListener = pageClickListener
        }

        /**
         * 初始化Adapter和设置当前选中的Item
         *
         * @param viewPager
         */
        fun setUpViewViewPager(viewPager: ViewPager?) {
            mViewPager = viewPager
            mViewPager?.adapter = this
            mViewPager?.adapter?.notifyDataSetChanged()
            val currentItem = if (canLoop) startSelectItem else 0
            //设置当前选中的Item
            mViewPager?.currentItem = currentItem
        }

        // 我们设置当前选中的位置为Integer.MAX_VALUE / 2,这样开始就能往左滑动
        // 但是要保证这个值与getRealPosition 的 余数为0，因为要从第一页开始显示
        // 直到找到从0开始的位置
        private val startSelectItem: Int
            get() {
                if (realCount == 0) {
                    return 0
                }
                // 我们设置当前选中的位置为Integer.MAX_VALUE / 2,这样开始就能往左滑动
                // 但是要保证这个值与getRealPosition 的 余数为0，因为要从第一页开始显示
                var currentItem = realCount * mLooperCountFactor / 2
                if (currentItem % realCount == 0) {
                    return currentItem
                }
                // 直到找到从0开始的位置
                while (currentItem % realCount != 0) {
                    currentItem++
                }
                return currentItem
            }

        fun setData(data: MutableList<T>) {
            mData.clear()
            mData.addAll(data)
        }


        override fun isViewFromObject(view: View, `object`: Any): Boolean {
            return view === `object`
        }

        override fun instantiateItem(container: ViewGroup, position: Int): Any {
            val view = getView(position, container)
            container.addView(view)
            return view
        }

        override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
            container.removeView(`object` as View?)
        }

        override fun getCount() =
            if (canLoop) realCount * mLooperCountFactor else realCount //ViewPager返回int 最大值

        override fun finishUpdate(container: ViewGroup) {
            // 轮播模式才执行
            if (canLoop) {
                var position = mViewPager?.currentItem
                if (position == count - 1) {
                    position = 0
                    setCurrentItem(position)
                }
            }
        }

        private fun setCurrentItem(position: Int) {
            try {
                mViewPager?.setCurrentItem(position, false)
            } catch (e: IllegalStateException) {
                e.printStackTrace()
            }
        }

        /**
         * 获取真实的Count
         *
         * @return
         */
        private val realCount: Int
            get() = mData.size

        /**
         * @param position
         * @param container
         * @return
         */
        private fun getView(position: Int, container: ViewGroup): View {
            val realPosition = position % realCount

            val view: View = factory.createView(container.context)
            if (mData.isNullOrEmpty().not()) {
                factory.onBind(view, realPosition, mData[realPosition])
            }

            // 添加点击事件
            view.setOnClickListener { v ->
                mPageClickListener?.invoke(v, realPosition)
            }
            return view
        }

        init {
            mData.addAll(datas)
        }
    }

    /**
     * ＊由于ViewPager 默认的切换速度有点快，因此用一个Scroller 来控制切换的速度
     *
     * 而实际上ViewPager 切换本来就是用的Scroller来做的，因此我们可以通过反射来
     *
     * 获取取到ViewPager 的 mScroller 属性，然后替换成我们自己的Scroller
     */
    class ViewPagerScroller : Scroller {
        var scrollDuration = 800 // ViewPager默认的最大Duration 为600,我们默认稍微大一点。值越大越慢。
            private set
        var useDefaultDuration = false

        constructor(context: Context?) : super(context) {}
        constructor(
            context: Context?,
            interpolator: Interpolator?
        ) : super(context, interpolator)

        constructor(
            context: Context?,
            interpolator: Interpolator?,
            flywheel: Boolean
        ) : super(context, interpolator, flywheel)

        override fun startScroll(startX: Int, startY: Int, dx: Int, dy: Int) {
            super.startScroll(startX, startY, dx, dy, scrollDuration)
        }

        override fun startScroll(
            startX: Int,
            startY: Int,
            dx: Int,
            dy: Int,
            duration: Int
        ) {
            super.startScroll(
                startX,
                startY,
                dx,
                dy,
                if (useDefaultDuration) duration else scrollDuration
            )
        }

        fun setDuration(duration: Int) {
            scrollDuration = duration
        }

    }

    companion object {
        private const val TAG = "MZBannerView"
        fun getScreenWidth(context: Context): Int {
            val resources = context.resources
            val dm = resources.displayMetrics
            return dm.widthPixels
        }


    }
}